# 了解异常
"""
1、程序运行时出现的错误
2、了解bug：bug即异常。因为小虫子引起的计算机失灵。
"""




# 异常的捕获
"""
捕获异常的作用在于:提前假设某处会出现异常，做好提前准备，当真的出现异常的时候，可以有后续手段。

基本语法：
try：
    可能发生错误的代码
excpet：
    如果出现异常执行的代码

示例：
try:
    open("pix","r")   # 用r模式打开文件
except:
    open("pix", "w")   # 用w模式打开，若文件不存在，则创建
    

# 捕获指定的异常
try:
    print(name)
except NameError as e:          # e就是异常的对象，通过as设置别名，就可以通过异常作为对象渠道
    print("出现了变量为定义的异常")
    print(e)      # name 'name' is not defined
    



# 捕获多个异常
try:
    print(1/0)
except (NameError, ZeroDivisionError) as e:     
    print("出现变量未定义 或者 除零的异常错误")


# 捕获全部异常(最上面的基本语法就是捕获全部异常的写法)
try:
    1/0
    
    # print(name)

except Exception as e:
    print("出现异常了")
    

# 异常else  # 未出现异常时应做的事情
try:
    print(1)                # 打印1
except Exception as e:
    print(e)

else:          
    print("我是else，是没有异常的时候执行的代码")
    


# 异常的finally
# finally表示的是无论是否异常都要执行的代码，例如关闭文件
try:
    f = open('test.txt', 'r')
except Exception as e:
    f = open('test.txt', 'w')
else:
    print("没有异常，真开心")
finally:
    f.close()
    

"""



# 异常的传递性
"""
def func01():
    print("这里是func01")
    num = 1 / 0
    print("func01结束")

def func02():
    print("这里是func02")
    func01()
    print("func02结束")

def main():
    try:
        func02()
    except  Exception as e:
        print(f"出现的异常是{e}")    # 出现的异常是division by zero

main()

异常的传递
异常是具有传递性的

当函数func01中发生异常，并且没有捕获处理这个异常的时候，异常会传递到函数func02，当func02也没有捕获处理这个异常的时候main函数会捕获这个异常，
这就是异常的传递性。

提示:
当所有函数都没有捕获异常的时候，程序就会报错
"""



# python模块
"""
1 python模块的导入
Python 模块(Module)，是一个python 文件，以.py结尾， 模块能定义函数，类和变量，模块里也能包含可执行的代码。

模块的作用: python中有很多各种不同的模块，每一个模块都可以帮助我们快速的实现一些功能，比如实现和时间相关的功能就可以使用time模块我们可以
认为一个模块就是一个工具包，每一个工具包中都有各种不同的工具供我们使用进而实现各种不同的功能。

大白话:模块就是一个Python文件，里面有类、函数、变量等，我们可以拿过来用(导入模块去使用)
导入模块一般写在文件开头位置

模块的导入方式：
模块在使用前需要先导入。
语法：[from 模块名] import[模块 | 类 | 变量 | 函数 | *（代表导入模块所有功能） ] [as 别名]
# []在语法中说明是可选的，但是import必须写。

常用的组合形式如:
import 模块名
from 模块名 import 类、变量、方法等
from 模块名 import *
import 模块名 as 别名
from 模块名 import 功能名 as别名

# import模块名
import time        # 导入python内置的time模块（本质上就是time.py这个代码文件）
print("你好")
time.sleep(5)      # 睡眠，中断执行。通过. 就可以使用模块内部的全部功能|代码。（类，函数，变量）
print("我好")

# from 模块名 import 功能名
from time import sleep   # 导入time模块中的sleep方法，其他功能不可用。
print("开始")
sleep(5)
print("结束")

# from 模块名 import *   # 导入time模块中所有的方法
from time import *
print("开始")
sleep(5)
print("结束")

# 模块定义别名：import 模块名 as 别名 (白话：给具体的模块或功能改名)
import time as tt
tt.sleep(5)
print("hello")

# 功能定义别名：from 模块名 import 功能 as 别名
from time import sleep as tt
tt(5)
print("hello")




2 自定义模块
Python中已经帮我们实现了很多的模块，不过有时候我们需要一些个性化的模块，这里就可以通过自定义模块实现，也就是自己制作一个模块。
每个Python文件都可以作为一个模块，模块的名字就是文件的名字，也就是说自定义模块名必须要符合标识符命名规则。

注意事项：导入不同模块的同名功能时，后面导入的功能会覆盖前面的功能。 


# 导入自定义模块使用
# 第一种方式
import ninth_add
ninth_add.test(1, 2)
# 第二种方式
from ninth_add import test
test(1, 2)


# 测试模块     __main__变量
在实际开发中，当一个开发人员编写完一个模块后，为了让模块能够在项目中达到想要的效果，这个开发人员会自行在py文件中添加一些测试信息，
例如，在my_module1.py文件中添加测试代码test(1,1)
def test(a, b):print(a + b)
test(1,1)
问题:此时，无论是当前文件，还是其他已经导入了该模块的文件，在运行的时候都会自动执行"test"函数的调用
解决方案：
def test(a, b)
    print(a+b)
    
# 只在当前文件中调用该函数，如果是被其他模块导入的，则不执行test函数调用 
    if __name__ == '__main__':
        test(1, 2)



# __all__变量
如果一个模块文件中有`__a11__'变量，当使用`from xxx import *导入时，只能导入这个列表中的元素，__all__变量是一个列表。

from ninth_add import *
test1(3, 4)     # 只能用test1功能，可以手动导入test2,例：from ninth_add import test2

"""




# python包
"""
1.自定义包
什么是Python包？
从物理上看，包就是一个文件夹，在该文件夹下包含了一个 _init_.py（有这个文件就是python包，没有就是文件夹） 文件，
该文件夹可用于包含多个模块文件从逻辑上看，包的本质依然是模块。

包的作用:当我们的模块文件越来越多时,包可以帮助我们管理这些模块，包的作用就是包含多个模块，但包的本质依然是模块

步骤如下:
① 新建包'my_package'
② 新建包内模块:'my_module1'、和'my_module2'


Pycharm中的基本步骤:
[New]→ [Python Package]→ 输入包名 →→ 新建功能模块(有联系的模块)
注意:新建包后，包内部会自动创建”_init_.py文件，这个文件控制着包的导入行为。



# 导入包
方式一
import 包名.模块名
包名.模块名.目标(功能)

# 导入自定义的包中的模块，并使用
import my_package.my_module1
import my_package.my_module2

my_package.my_module1.info_print1()
my_package.my_module2.info_print2()

# 可以加入from
from my_package import my_module1
from my_package import my_module2

my_module1.info_print1()
my_module2.info_print2()

# 还有可以from my_package.my_module2 import info_print2这样写



方式二
注意:必须在`_init_.py`文件中添加`__al1__= [ ]`，控制允许导入的模块列表

from 包名 import *
模块名.目标

# 通过__all__变量，控制import *
from my_package import *
my_module2.info_print2()

"""


# 安装第三方包
"""
我们知道，包可以包含一堆的Python模块，而每个模块又内含许多的功能。
所以，我们可以认为:一个包，就是一堆同类型功能的集合体。
在Python程序的生态中，有许多非常多的第三方包(非Python官方)，可以极大的帮助我们提高开发效率，如:
科学计算中常用的:numpy包
数据分析中常用的:pandas包
大数据计算中常用的:pyspark、apache-flink包
图形可视化常用的:matplotlib、pyecharts
人工智能常用的:tensorflow
等
这些第三方的包，极大的丰富了Python的生态，提高了开发效率。
但是由于是第三方，所以Python没有内置，所以我们需要安装它们才可以导入使用哦。


安装第三方包：pip
第三方包的安装非常简单，我们只需要使用Python内置的pip程序即可
打开我们许久未见的:命令提示符程序，在里面输入:
pip install 包名称
即可通过网络快速安装第三方包


pip的网络优化
由于pip是连接的国外的网站进行包的下载，所以有的时候会速度很慢。
我们可以通过如下命令，让其连接国内的网站进行包的安装:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称
上网址是清华大学提供的一个网站，可供pip程序下载第三方包


"""


# 综合案例
"""
创建一个自定义包，名称为:my_utils(我的工具)
在包内提供2个模块
str_util.py(字符串相关工具，内含:)
    函数:str_reverse(s)，接受传入字符串，将字符串反转返回
    函数:substr(s，x，y)，按照下标x和y，对字符串进行切片
file_util.py(文件处理相关工具，内含:)
    函数:print_file_info(file_name)-，接收传入文件的路径，打印文件的全部内.容，如文件不存在则捕获异常，输出提示信息，
通过finally关闭文件对象
    函数:append to_file(file_name，data)，接收文件路径以及传入数据，将数据追加写入到文件中
"""

# import my_utils.str_util
from my_utils import file_util

# my_utils.str_util.str_severse("黑马程序员")
# print(my_utils.str_util.substr("itheima", 0, 4))

file_util.append_to_file("D:/qqq.txt", "许德清是帅哥")
file_util.print_file_info("D:/qqq.txt")

